package com.cantor.cantorspringbootstarter.processor;

import cn.hutool.core.util.StrUtil;
import com.cantor.cantorspringbootstarter.anno.CantorReference;
import com.cantor.cantorspringbootstarter.tool.InjectionUtil;
import com.cantor.consumer.discovery.ServiceDiscoverer;
import com.cantor.consumer.pojo.ServiceRef;
import com.cantor.core.center.RegistrationCenter;
import com.cantor.core.config.CantorAppConfig;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Field;

@Slf4j
public class ReferenceInjectionProcessor implements BeanPostProcessor {

    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    CantorAppConfig cantorAppConfig;

    @Autowired
    RegistrationCenter center;

    @Autowired
    ServiceDiscoverer serviceDiscoverer;

    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        injectReference(bean);
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    void injectReference(Object bean) throws BeansException, IllegalAccessException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field f : fields) {
            if (!(f.getType().isInterface() && f.isAnnotationPresent(CantorReference.class))) {
                continue;
            }
            f.setAccessible(true);
            Class service = f.getType();
            CantorReference referenceAnn = f.getDeclaredAnnotation(CantorReference.class);
            ServiceRef serviceRef = new ServiceRef()
                    .setService(service)
                    .setAppConfig(cantorAppConfig)
                    .setCenter(center)
                    .setDiscoverer(serviceDiscoverer);
            String version = referenceAnn.version();
            if (StrUtil.isNotBlank(version)) {
                serviceRef.setVersion(version);
            }
            String str = referenceAnn.loadBalance();
            if (StrUtil.isNotBlank(str)) {
                serviceRef.setLoadBalance(str);
            }
            int timeout = referenceAnn.timeout();
            if (!InjectionUtil.isDefaultValue(timeout)) {
                serviceRef.setTimeout(timeout);
            }
            int retries = referenceAnn.retries();
            if (!InjectionUtil.isDefaultValue(retries)) {
                serviceRef.setRetries(retries);
            }
            String mock = referenceAnn.mock();
            if (StrUtil.isNotBlank(mock)) {
                serviceRef.setMock(mock);
            }
            doInjection(bean, f, serviceRef.get());
        }
    }

    <T> void doInjection(Object bean, Field field, T serviceImpl) throws IllegalAccessException {
        field.set(bean, serviceImpl);
    }

}
